import utils from './utils.js';
import browser, { ie } from './browser.js';
import dtd from './dtd.js';

function getDomNode(node, start, ltr, startFromChild, fn, guard) {
    var tmpNode = startFromChild && node[start],
        parent;
    !tmpNode && (tmpNode = node[ltr]);
    while (!tmpNode && (parent = (parent || node).parentNode)) {
        if (parent.tagName == "BODY" || (guard && !guard(parent))) {
            return null;
        }
        tmpNode = parent[ltr];
    }
    if (tmpNode && fn && !fn(tmpNode)) {
        return getDomNode(tmpNode, start, ltr, false, fn);
    }
    return tmpNode;
}
var attrFix = ie && browser.version < 9
  ? {
      tabindex: "tabIndex",
      readonly: "readOnly",
      for: "htmlFor",
      class: "className",
      maxlength: "maxLength",
      cellspacing: "cellSpacing",
      cellpadding: "cellPadding",
      rowspan: "rowSpan",
      colspan: "colSpan",
      usemap: "useMap",
      frameborder: "frameBorder"
    }
  : {
      tabindex: "tabIndex",
      readonly: "readOnly"
    },

  styleBlock = utils.listToMap([
    "-webkit-box",
    "-moz-box",
    "block",
    "list-item",
    "table",
    "table-row-group",
    "table-header-group",
    "table-footer-group",
    "table-row",
    "table-column-group",
    "table-column",
    "table-cell",
    "table-caption"
  ]);

var styleBlock = utils.listToMap([
    "-webkit-box",
    "-moz-box",
    "block",
    "list-item",
    "table",
    "table-row-group",
    "table-header-group",
    "table-footer-group",
    "table-row",
    "table-column-group",
    "table-column",
    "table-cell",
    "table-caption"
]);

const domUtils = {

    NODE_ELEMENT: 1,
    NODE_DOCUMENT: 9,
    NODE_TEXT: 3,
    NODE_COMMENT: 8,
    NODE_DOCUMENT_FRAGMENT: 11,

    //位置关系
    POSITION_IDENTICAL: 0,
    POSITION_DISCONNECTED: 1,
    POSITION_FOLLOWING: 2,
    POSITION_PRECEDING: 4,
    POSITION_IS_CONTAINED: 8,
    POSITION_CONTAINS: 16,
    //ie6使用其他的会有一段空白出现
    fillChar: ie && browser.version == "6" ? "\ufeff" : "\u200B",

    /**
     * 获取节点node所属的window对象
     * @method  getWindow
     * @param { Node } node 节点对象
     * @return { Window } 当前节点所属的window对象
     * @example
     * ```javascript
     * //output: true
     * console.log( UE.dom.domUtils.getWindow( document.body ) === window );
     * ```
     */
    getWindow: function (node) {
        var doc = node.ownerDocument || node;
        return doc.defaultView || doc.parentWindow;
    },
    on: function (element, type, handler) {
        var types = utils.isArray(type) ? type : utils.trim(type).split(/\s+/),
            k = types.length;
        if (k)
            while (k--) {
                type = types[k];
                if (element.addEventListener) {
                    element.addEventListener(type, handler, false);
                } else {
                    if (!handler._d) {
                        handler._d = {
                            els: []
                        };
                    }
                    var key = type + handler.toString(),
                        index = utils.indexOf(handler._d.els, element);
                    if (!handler._d[key] || index == -1) {
                        if (index == -1) {
                            handler._d.els.push(element);
                        }
                        if (!handler._d[key]) {
                            handler._d[key] = function (evt) {
                                return handler.call(evt.srcElement, evt || window.event);
                            };
                        }

                        element.attachEvent("on" + type, handler._d[key]);
                    }
                }
            }
        element = null;
    },
    /**
       * 解除DOM事件绑定
       * @method un
       * @param { Node } element 需要解除事件绑定的节点对象
       * @param { String } type 需要接触绑定的事件类型
       * @param { Function } handler 对应的事件处理器
       * @example
       * ```javascript
       * UE.dom.domUtils.un(document.body,"click",function(evt){
       *     //evt为事件对象，this为被点击元素对象
       * });
       * ```
       */

    /**
       * 解除DOM事件绑定
       * @method un
       * @param { Node } element 需要解除事件绑定的节点对象
       * @param { Array } type 需要接触绑定的事件类型数组
       * @param { Function } handler 对应的事件处理器
       * @example
       * ```javascript
       * UE.dom.domUtils.un(document.body, ["click","mousedown"],function(evt){
       *     //evt为事件对象，this为被点击元素对象
       * });
       * ```
       */
    un: function (element, type, handler) {
        var types = utils.isArray(type) ? type : utils.trim(type).split(/\s+/),
            k = types.length;
        if (k)
            while (k--) {
                type = types[k];
                if (element.removeEventListener) {
                    element.removeEventListener(type, handler, false);
                } else {
                    var key = type + handler.toString();
                    try {
                        element.detachEvent(
                            "on" + type,
                            handler._d ? handler._d[key] : handler
                        );
                    } catch (e) { }
                    if (handler._d && handler._d[key]) {
                        var index = utils.indexOf(handler._d.els, element);
                        if (index != -1) {
                            handler._d.els.splice(index, 1);
                        }
                        handler._d.els.length == 0 && delete handler._d[key];
                    }
                }
            }
    },
    isSameElement: function (nodeA, nodeB) {
        if (nodeA.tagName != nodeB.tagName) {
            return false;
        }
        var thisAttrs = nodeA.attributes,
            otherAttrs = nodeB.attributes;
        if (!ie && thisAttrs.length != otherAttrs.length) {
            return false;
        }
        var attrA,
            attrB,
            al = 0,
            bl = 0;
        for (var i = 0; (attrA = thisAttrs[i++]);) {
            if (attrA.nodeName == "style") {
                if (attrA.specified) {
                    al++;
                }
                if (domUtils.isSameStyle(nodeA, nodeB)) {
                    continue;
                } else {
                    return false;
                }
            }
            if (ie) {
                if (attrA.specified) {
                    al++;
                    attrB = otherAttrs.getNamedItem(attrA.nodeName);
                } else {
                    continue;
                }
            } else {
                attrB = nodeB.attributes[attrA.nodeName];
            }
            if (!attrB.specified || attrA.nodeValue != attrB.nodeValue) {
                return false;
            }
        }
        // 有可能attrB的属性包含了attrA的属性之外还有自己的属性
        if (ie) {
            for (i = 0; (attrB = otherAttrs[i++]);) {
                if (attrB.specified) {
                    bl++;
                }
            }
            if (al != bl) {
                return false;
            }
        }
        return true;
    },

    /**
    * 获取节点A相对于节点B的位置关系
    * @method getPosition
    * @param { Node } nodeA 需要查询位置关系的节点A
    * @param { Node } nodeB 需要查询位置关系的节点B
    * @return { Number } 节点A与节点B的关系
    * @example
    * ```javascript
    * //output: 20
    * var position = UE.dom.domUtils.getPosition( document.documentElement, document.body );
    *
    * switch ( position ) {
    *
    *      //0
    *      case UE.dom.domUtils.POSITION_IDENTICAL:
    *          console.log('元素相同');
    *          break;
    *      //1
    *      case UE.dom.domUtils.POSITION_DISCONNECTED:
    *          console.log('两个节点在不同的文档中');
    *          break;
    *      //2
    *      case UE.dom.domUtils.POSITION_FOLLOWING:
    *          console.log('节点A在节点B之后');
    *          break;
    *      //4
    *      case UE.dom.domUtils.POSITION_PRECEDING;
    *          console.log('节点A在节点B之前');
    *          break;
    *      //8
    *      case UE.dom.domUtils.POSITION_IS_CONTAINED:
    *          console.log('节点A被节点B包含');
    *          break;
    *      case 10:
    *          console.log('节点A被节点B包含且节点A在节点B之后');
    *          break;
    *      //16
    *      case UE.dom.domUtils.POSITION_CONTAINS:
    *          console.log('节点A包含节点B');
    *          break;
    *      case 20:
    *          console.log('节点A包含节点B且节点A在节点B之前');
    *          break;
    *
    * }
    * ```
    */
    getPosition: function (nodeA, nodeB) {
        // 如果两个节点是同一个节点
        if (nodeA === nodeB) {
            // domUtils.POSITION_IDENTICAL
            return 0;
        }
        var node,
            parentsA = [nodeA],
            parentsB = [nodeB];
        node = nodeA;
        while ((node = node.parentNode)) {
            // 如果nodeB是nodeA的祖先节点
            if (node === nodeB) {
                // domUtils.POSITION_IS_CONTAINED + domUtils.POSITION_FOLLOWING
                return 10;
            }
            parentsA.push(node);
        }
        node = nodeB;
        while ((node = node.parentNode)) {
            // 如果nodeA是nodeB的祖先节点
            if (node === nodeA) {
                // domUtils.POSITION_CONTAINS + domUtils.POSITION_PRECEDING
                return 20;
            }
            parentsB.push(node);
        }
        parentsA.reverse();
        parentsB.reverse();
        if (parentsA[0] !== parentsB[0]) {
            // domUtils.POSITION_DISCONNECTED
            return 1;
        }
        var i = -1;
        while ((i++, parentsA[i] === parentsB[i])) { }
        nodeA = parentsA[i];
        nodeB = parentsB[i];
        while ((nodeA = nodeA.nextSibling)) {
            if (nodeA === nodeB) {
                // domUtils.POSITION_PRECEDING
                return 4;
            }
        }
        // domUtils.POSITION_FOLLOWING
        return 2;
    },

    inDoc(node, doc) {
        return domUtils.getPosition(node, doc) == 10;
    },

    filterNodeList: function (nodelist, filter, forAll) {
        var results = [];
        if (!utils.isFunction(filter)) {
            var str = filter;
            filter = function (n) {
                return (
                    utils.indexOf(
                        utils.isArray(str) ? str : str.split(" "),
                        n.tagName.toLowerCase()
                    ) != -1
                );
            };
        }
        utils.each(nodelist, function (n) {
            filter(n) && results.push(n);
        });
        return results.length == 0
            ? null
            : results.length == 1 || !forAll ? results[0] : results;
    },


    findParents: function (node, includeSelf, filterFn, closerFirst) {
        var parents = includeSelf && ((filterFn && filterFn(node)) || !filterFn)
            ? [node]
            : [];
        while ((node = domUtils.findParent(node, filterFn))) {
            parents.push(node);
        }
        return closerFirst ? parents : parents.reverse();
    },

    isBookmarkNode: function (node) {
        return node.nodeType == 1 && node.id && /^_baidu_bookmark_/i.test(node.id);
    },

    clearEmptySibling: function (node, ignoreNext, ignorePre) {
        function clear(next, dir) {
            var tmpNode;
            while (
                next &&
                !domUtils.isBookmarkNode(next) &&
                (domUtils.isEmptyInlineElement(next) ||
                    //这里不能把空格算进来会吧空格干掉，出现文字间的空格丢掉了
                    !new RegExp("[^\t\n\r" + domUtils.fillChar + "]").test(
                        next.nodeValue
                    ))
            ) {
                tmpNode = next[dir];
                domUtils.remove(next);
                next = tmpNode;
            }
        }
        !ignoreNext && clear(node.nextSibling, "nextSibling");
        !ignorePre && clear(node.previousSibling, "previousSibling");
    },


    isEmptyInlineElement: function (node) {
        if (node.nodeType != 1 || !dtd.$removeEmpty[node.tagName]) {
            return 0;
        }
        node = node.firstChild;
        while (node) {
            //如果是创建的bookmark就跳过
            if (domUtils.isBookmarkNode(node)) {
                return 0;
            }
            if (
                (node.nodeType == 1 && !domUtils.isEmptyInlineElement(node)) ||
                (node.nodeType == 3 && !domUtils.isWhitespace(node))
            ) {
                return 0;
            }
            node = node.nextSibling;
        }
        return 1;
    },
    mergeSibling: function (node, ignorePre, ignoreNext) {
        function merge(rtl, start, node) {
            var next;
            if (
                (next = node[rtl]) &&
                !domUtils.isBookmarkNode(next) &&
                next.nodeType == 1 &&
                domUtils.isSameElement(node, next)
            ) {
                while (next.firstChild) {
                    if (start == "firstChild") {
                        node.insertBefore(next.lastChild, node.firstChild);
                    } else {
                        node.appendChild(next.firstChild);
                    }
                }
                domUtils.remove(next);
            }
        }
        !ignorePre && merge("previousSibling", "firstChild", node);
        !ignoreNext && merge("nextSibling", "lastChild", node);
    },
    mergeChild: function (node, tagName, attrs) {
        var list = domUtils.getElementsByTagName(node, node.tagName.toLowerCase());
        for (var i = 0, ci; (ci = list[i++]);) {
            if (!ci.parentNode || domUtils.isBookmarkNode(ci)) {
                continue;
            }
            //span单独处理
            if (ci.tagName.toLowerCase() == "span") {
                if (node === ci.parentNode) {
                    domUtils.trimWhiteTextNode(node);
                    if (node.childNodes.length == 1) {
                        node.style.cssText = ci.style.cssText + ";" + node.style.cssText;
                        domUtils.remove(ci, true);
                        continue;
                    }
                }
                ci.style.cssText = node.style.cssText + ";" + ci.style.cssText;
                if (attrs) {
                    var style = attrs.style;
                    if (style) {
                        style = style.split(";");
                        for (var j = 0, s; (s = style[j++]);) {
                            ci.style[utils.cssStyleToDomStyle(s.split(":")[0])] = s.split(
                                ":"
                            )[1];
                        }
                    }
                }
                if (domUtils.isSameStyle(ci, node)) {
                    domUtils.remove(ci, true);
                }
                continue;
            }
            if (domUtils.isSameElement(node, ci)) {
                domUtils.remove(ci, true);
            }
        }
    },

    getElementsByTagName: function (node, name, filter) {
        if (filter && utils.isString(filter)) {
            var className = filter;
            filter = function (node) {
                return domUtils.hasClass(node, className);
            };
        }
        name = utils.trim(name).replace(/[ ]{2,}/g, " ").split(" ");
        var arr = [];
        for (var n = 0, ni; (ni = name[n++]);) {
            var list = node.getElementsByTagName(ni);
            for (var i = 0, ci; (ci = list[i++]);) {
                if (!filter || filter(ci)) arr.push(ci);
            }
        }

        return arr;
    },
    findParent: function (node, filterFn, includeSelf) {
        if (node && !domUtils.isBody(node)) {
            node = includeSelf ? node : node.parentNode;
            while (node) {
                if (!filterFn || filterFn(node) || domUtils.isBody(node)) {
                    return filterFn && !filterFn(node) && domUtils.isBody(node)
                        ? null
                        : node;
                }
                node = node.parentNode;
            }
        }
        return null;
    },

    mergeToParent: function (node) {
        var parent = node.parentNode;
        while (parent && dtd.$removeEmpty[parent.tagName]) {
            if (parent.tagName == node.tagName || parent.tagName == "A") {
                //针对a标签单独处理
                domUtils.trimWhiteTextNode(parent);
                //span需要特殊处理  不处理这样的情况 <span stlye="color:#fff">xxx<span style="color:#ccc">xxx</span>xxx</span>
                if (
                    (parent.tagName == "SPAN" && !domUtils.isSameStyle(parent, node)) ||
                    (parent.tagName == "A" && node.tagName == "SPAN")
                ) {
                    if (parent.childNodes.length > 1 || parent !== node.parentNode) {
                        node.style.cssText =
                            parent.style.cssText + ";" + node.style.cssText;
                        parent = parent.parentNode;
                        continue;
                    } else {
                        parent.style.cssText += ";" + node.style.cssText;
                        //trace:952 a标签要保持下划线
                        if (parent.tagName == "A") {
                            parent.style.textDecoration = "underline";
                        }
                    }
                }
                if (parent.tagName != "A") {
                    parent === node.parentNode && domUtils.remove(node, true);
                    break;
                }
            }
            parent = parent.parentNode;
        }
    },


    isBody: function (node) {
        return node && node.nodeType == 1 && node.tagName.toLowerCase() == "body";
    },

    isBlockElm: function (node) {
        return (
            node.nodeType == 1 &&
            (dtd.$block[node.tagName] ||
                styleBlock[domUtils.getComputedStyle(node, "display")]) &&
            !dtd.$nonChild[node.tagName]
        );
    },

    getComputedStyle: function(element, styleName) {
        //一下的属性单独处理
        var pros = "width height top left";
    
        if (pros.indexOf(styleName) > -1) {
          return (
            element[
              "offset" +
                styleName.replace(/^\w/, function(s) {
                  return s.toUpperCase();
                })
            ] + "px"
          );
        }
        //忽略文本节点
        if (element.nodeType == 3) {
          element = element.parentNode;
        }
        //ie下font-size若body下定义了font-size，则从currentStyle里会取到这个font-size. 取不到实际值，故此修改.
        if (
          browser.ie &&
          browser.version < 9 &&
          styleName == "font-size" &&
          !element.style.fontSize &&
          !dtd.$empty[element.tagName] &&
          !dtd.$nonChild[element.tagName]
        ) {
          var span = element.ownerDocument.createElement("span");
          span.style.cssText = "padding:0;border:0;font-family:simsun;";
          span.innerHTML = ".";
          element.appendChild(span);
          var result = span.offsetHeight;
          element.removeChild(span);
          span = null;
          return result + "px";
        }
        try {
          var value =
            domUtils.getStyle(element, styleName) ||
            (window.getComputedStyle
              ? domUtils
                  .getWindow(element)
                  .getComputedStyle(element, "")
                  .getPropertyValue(styleName)
              : (element.currentStyle || element.style)[
                  utils.cssStyleToDomStyle(styleName)
                ]);
        } catch (e) {
          return "";
        }
        return utils.transUnitToPx(utils.fixColor(styleName, value));
      },

    /**
  * 将一个文本节点textNode拆分成两个文本节点，offset指定拆分位置
  * @method split
  * @param { Node } textNode 需要拆分的文本节点对象
  * @param { int } offset 需要拆分的位置， 位置计算从0开始
  * @return { Node } 拆分后形成的新节点
  * @example
  * ```html
  * <div id="test">abcdef</div>
  * <script>
  *      var newNode = UE.dom.domUtils.split( document.getElementById( "test" ).firstChild, 3 );
  *      //output: def
  *      console.log( newNode.nodeValue );
  * </script>
  * ```
  */
    split: function (node, offset) {
        var doc = node.ownerDocument;
        if (browser.ie && offset == node.nodeValue.length) {
            var next = doc.createTextNode("");
            return domUtils.insertAfter(node, next);
        }
        var retval = node.splitText(offset);
        //ie8下splitText不会跟新childNodes,我们手动触发他的更新
        if (browser.ie8) {
            var tmpNode = doc.createTextNode("");
            domUtils.insertAfter(retval, tmpNode);
            domUtils.remove(tmpNode);
        }
        return retval;
    },

    remove: function (node, keepChildren) {
        var parent = node.parentNode,
            child;
        if (parent) {
            if (keepChildren && node.hasChildNodes()) {
                while ((child = node.firstChild)) {
                    parent.insertBefore(child, node);
                }
            }
            parent.removeChild(node);
        }
        return node;
    },


    getNodeIndex: function (node, ignoreTextNode) {
        var preNode = node,
            i = 0;
        while ((preNode = preNode.previousSibling)) {
            if (ignoreTextNode && preNode.nodeType == 3) {
                if (preNode.nodeType != preNode.nextSibling.nodeType) {
                    i++;
                }
                continue;
            }
            i++;
        }
        return i;
    },

    getNextDomNode: function (node, startFromChild, filterFn, guard) {
        return getDomNode(
            node,
            "firstChild",
            "nextSibling",
            startFromChild,
            filterFn,
            guard
        );
    },

    isWhitespace: function (node) {
        return !new RegExp("[^ \t\n\r" + domUtils.fillChar + "]").test(
            node.nodeValue
        );
    },

    breakParent: function(node, parent) {
        var tmpNode,
          parentClone = node,
          clone = node,
          leftNodes,
          rightNodes;
        do {
          parentClone = parentClone.parentNode;
          if (leftNodes) {
            tmpNode = parentClone.cloneNode(false);
            tmpNode.appendChild(leftNodes);
            leftNodes = tmpNode;
            tmpNode = parentClone.cloneNode(false);
            tmpNode.appendChild(rightNodes);
            rightNodes = tmpNode;
          } else {
            leftNodes = parentClone.cloneNode(false);
            rightNodes = leftNodes.cloneNode(false);
          }
          while ((tmpNode = clone.previousSibling)) {
            leftNodes.insertBefore(tmpNode, leftNodes.firstChild);
          }
          while ((tmpNode = clone.nextSibling)) {
            rightNodes.appendChild(tmpNode);
          }
          clone = parentClone;
        } while (parent !== parentClone);
        tmpNode = parent.parentNode;
        tmpNode.insertBefore(leftNodes, parent);
        tmpNode.insertBefore(rightNodes, parent);
        tmpNode.insertBefore(node, rightNodes);
        domUtils.remove(parent);
        return node;
      },

      removeStyle: function(element, name) {
        if (browser.ie) {
          //针对color先单独处理一下
          if (name == "color") {
            name = "(^|;)" + name;
          }
          element.style.cssText = element.style.cssText.replace(
            new RegExp(name + "[^:]*:[^;]+;?", "ig"),
            ""
          );
        } else {
          if (element.style.removeProperty) {
            element.style.removeProperty(name);
          } else {
            element.style.removeAttribute(utils.cssStyleToDomStyle(name));
          }
        }
    
        if (!element.style.cssText) {
          domUtils.removeAttributes(element, ["style"]);
        }
      },

      removeAttributes: function(node, attrNames) {
        attrNames = utils.isArray(attrNames)
          ? attrNames
          : utils.trim(attrNames).replace(/[ ]{2,}/g, " ").split(" ");
        for (var i = 0, ci; (ci = attrNames[i++]); ) {
          ci = attrFix[ci] || ci;
          switch (ci) {
            case "className":
              node[ci] = "";
              break;
            case "style":
              node.style.cssText = "";
              var val = node.getAttributeNode("style");
              !browser.ie && val && node.removeAttributeNode(val);
          }
          node.removeAttribute(ci);
        }
      },

      isEmptyNode: function(node) {
        return (
          !node.firstChild ||
          domUtils.getChildCount(node, function(node) {
            return (
              !domUtils.isBr(node) &&
              !domUtils.isBookmarkNode(node) &&
              !domUtils.isWhitespace(node)
            );
          }) == 0
        );
      },

      getChildCount: function(node, fn) {
        var count = 0,
          first = node.firstChild;
        fn =
          fn ||
          function() {
            return 1;
          };
        while (first) {
          if (fn(first)) {
            count++;
          }
          first = first.nextSibling;
        }
        return count;
      },

      isBr: function(node) {
        return node.nodeType == 1 && node.tagName == "BR";
      },
      isBookmarkNode: function(node) {
        return node.nodeType == 1 && node.id && /^_baidu_bookmark_/i.test(node.id);
      },
      isWhitespace: function(node) {
        return !new RegExp("[^ \t\n\r" + domUtils.fillChar + "]").test(
          node.nodeValue
        );
      },

}

export default domUtils;